Bad weather Kelheim Demo

Oleksandr Soboliev

Research Meteostat

After some researches about meteostat data nearest station in DE that belongs to Kelheim region are:

there are many of them so I am starting to think about extracting all from the Bayern or extract the nearest from longtitude/latitude point with the Kelheim shapefile(using json and Euclid distances)

Kelheim has no weather station, but it could be reconstructed with 2 other

Hohenfels with id: “10775” and Ingolstadt with id:“10860” kelheim_data = {weight1}x{hohenfels} + {weight2}x{inglstadt}

Also this site shows, that there are many of the Kelheim stations in this area, but meteostat doesn’t contain them https://www.wunderground.com/dashboard/pws/IKELHE5

Research Weatherstack

weatherstack_kelheim = read_delim("data/Kelheim_weather_since_july_2008.csv",delim = ",")
Rows: 120312 Columns: 6── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (1): description
dbl  (4): hour, precip, visibility, totalsnow_daily
date (1): date
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
print(weatherstack_kelheim)

What to take as a reffer point isn’t clear because of the date(before/after covid) and weather type (sunny,clear,temperature) Also there is no temperature in it :/

Import mobility from Google

#global_mobility = read_delim("https://www.gstatic.com/covid19/mobility/Global_Mobility_Report.csv",",")
#de_mobility = global_mobility %>% filter(country_region_code == "DE")
#print(unique(de_mobility$sub_region_1))

As we can see the most precise region to filter data from is Bavaria :/

Relevant data for the , mobility

#bavaria_mobility = de_mobility %>% filter(sub_region_1 == "Bavaria")
#bavaria_mobility = bavaria_mobility %>% #select(country_region,sub_region_1,date,residential_percent_change_from_baseline) %>%
#  mutate(residential_percent_change_from_baseline = -residential_percent_change_from_baseline,
#         source = "Google")%>%
#  rename(BundeslandID = sub_region_1,not_at_home_change = residential_percent_change_from_baseline)
#bavaria_mobility = bavaria_mobility %>% select(date,BundeslandID,not_at_home_change,source)
#Need to filter out weekends

#plt = ggplot(bavaria_mobility)+
#  geom_point(aes(x = date,y = not_at_home_change))
#ggplotly(plt)

Import mobility from Senozon

snz_mobility = read_delim("data/LK_mobilityData_weekdays.csv",";")
Rows: 50652 Columns: 4── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ";"
chr (1): Landkreis
dbl (3): date, outOfHomeDuration, percentageChangeComparedToBeforeCorona
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
#Kelheim
snz_mobility_kelheim = snz_mobility %>% filter(Landkreis == "Landkreis Kelheim") %>% mutate(source = "senozon") %>% select(-outOfHomeDuration) %>% rename(not_at_home_change = percentageChangeComparedToBeforeCorona)
snz_mobility_kelheim$date = as.Date(strptime(snz_mobility_kelheim$date,"%Y%m%d"))

#Berlin
snz_mobility_berlin = snz_mobility %>% filter(Landkreis == "Berlin") %>% mutate(source = "senozon") %>% select(-outOfHomeDuration) %>% rename(not_at_home_change = percentageChangeComparedToBeforeCorona)
snz_mobility_berlin$date = as.Date(strptime(snz_mobility_berlin$date,"%Y%m%d"))

colors <- c("Berlin" = "blue", "Kelheim" = "red")

plt = ggplot()+
  geom_point(data = snz_mobility_kelheim,aes(x = date,y = not_at_home_change,color = "Berlin"))+
  geom_point(data = snz_mobility_berlin,aes(x = date,y = not_at_home_change,color = "Kelheim"))+
  scale_colour_manual(values = colors)
ggplotly(plt)

Aggregate 2 sources

We take berlin weather from station in Schoenefeld with id = 10384

Rows: 24422 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl  (10): X2, X3, X4, X5, X6, X7, X8, X9, X10, X11
date  (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# think about duration of description column
# Kelheim
weatherstack_kelheim_daily = weatherstack_kelheim %>%
  group_by(date) %>%
  summarize(description = description,precip_day = sum(precip),visibility_mean = mean(visibility),totalsnow_daily = mean(totalsnow_daily))
`summarise()` has grouped output by 'date'. You can override using the `.groups` argument.
  
weatherstack_kelheim_weekly = weatherstack_kelheim_daily %>% 
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(description = description,date = first(date), precip_week = sum(precip_day),visibility_mean = mean(visibility_mean),totalsnow_weekly =sum( totalsnow_daily))
`summarise()` has grouped output by 'year_week'. You can override using the `.groups` argument.
weatherstack_kelheim_weekly = unique(weatherstack_kelheim_weekly)
print(weatherstack_kelheim_weekly)

#Berlin
  
berlin_weather_weekly = berlin_weather_daily %>% filter(year(date) >=2020) %>%
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(date = first(date), prcp_week = sum(prcp), tavg= mean(tavg),snow_week =sum( snow),wspd = mean(wspd),tmax = max(tmax)) %>%
  arrange(year_week)
print(berlin_weather_weekly)
NA
#mob_joined = rbind(snz_mobility_kelheim,bavaria_mobility)
#Kelheim
snz_mobility_kelheim_year_week = snz_mobility_kelheim %>% 
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(date = first(date),not_at_home_change = mean(not_at_home_change))
mob_joined_with_weather_kelheim = snz_mobility_kelheim_year_week %>% inner_join(weatherstack_kelheim_weekly, by = "year_week") %>% select(-date.y) %>% rename(date = date.x)
print(mob_joined_with_weather_kelheim)

#Berlin
snz_mobility_berlin_year_week = snz_mobility_berlin %>% 
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(date = first(date),not_at_home_change = mean(not_at_home_change))
mob_joined_with_weather_berlin = snz_mobility_berlin_year_week %>% inner_join(berlin_weather_weekly, by = "year_week") %>% select(-date.y) %>% rename(date = date.x)
print(mob_joined_with_weather_berlin)
#First plot with colour as precipitation
shapes <- c("Berlin" = 5, "Kelheim" = 3)
plt_color = ggplot()+
  geom_point(data = mob_joined_with_weather_kelheim,aes(x = date,y = not_at_home_change,colour = precip_week,shape = "Kelheim"))+
  #geom_point(data = mob_joined_with_weather_berlin,aes(x = date,y = not_at_home_change,colour = prcp_week,shape = "Berlin"))+
  scale_color_gradient2()+
  scale_shape_manual(values = shapes)
  

ggplotly(plt_color)
#Second plot as another line as precipitation
plt_line = ggplot(mob_joined_with_weather_kelheim)+
  geom_point(aes(x = date,y = not_at_home_change))+
  geom_line(aes(x = date,y = precip_week*0.5,color = "red"))
  

ggplotly(plt_line)
plt_hist_precip = ggplot(mob_joined_with_weather_kelheim,aes(x = precip_week,y = not_at_home_change))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 2,fill = "blue")

ggplotly(plt_hist_precip)
plt_hist_visibility = ggplot(mob_joined_with_weather_kelheim,aes(x = visibility_mean,y = not_at_home_change))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 0.5,fill = "blue")

ggplotly(plt_hist_visibility)
plt_hist_totalsnow = ggplot(mob_joined_with_weather_kelheim,aes(x = totalsnow_weekly,y = not_at_home_change))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 7,fill = "blue")

ggplotly(plt_hist_totalsnow)
#this is a bad plot because it takes description of 1 day of the week
plt_hist_descr = ggplot(mob_joined_with_weather_kelheim,aes(x = description,y = not_at_home_change))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 5,fill = "blue")+
  coord_flip()

ggplotly(plt_hist_descr)

Let’s try it out with meteostat data, that contains scope of the 2022 without restictions

Ingolstadt data from id = 10860 station

ingolstadt_weather = read_delim("https://bulk.meteostat.net/v2/daily/10860.csv.gz",",",col_names = FALSE)
Rows: 19288 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl  (9): X2, X3, X4, X5, X6, X7, X8, X9, X10
lgl  (1): X11
date (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
colnames(ingolstadt_weather) = c("date", "tavg", "tmin", "tmax", "prcp", "snow", "wdir", "wspd", "wpgt", "pres", "tsun")


# We don't need data of weather before 2020, because of snz_mobility date, also data isn't precise

ingolstadt_weather = ingolstadt_weather %>% filter(year(date)>=2020)%>% replace_na(list(snow = 0))

print(ingolstadt_weather)

Hohenfels data from id = 10775 station

hohenfels_weather = read_delim("https://bulk.meteostat.net/v2/daily/10775.csv.gz",",",col_names = FALSE)
Rows: 6311 Columns: 11── Column specification ─────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
dbl  (8): X2, X3, X4, X5, X6, X7, X8, X10
lgl  (2): X9, X11
date (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
colnames(hohenfels_weather) = c("date", "tavg", "tmin", "tmax", "prcp", "snow", "wdir", "wspd", "wpgt", "pres", "tsun")


# We don't need data of weather before 2020, because of snz_mobility date, also data isn't precise

hohenfels_weather = hohenfels_weather %>% filter(year(date)>=2020) %>% replace_na(list(snow = 0))

print(hohenfels_weather)

As we can see in Hohenfels data isn’t that accurate and precipitation is data is missing fr year 2020, so for the further analysis we take only Ingolstadt data.

ingolstadt_weather_weekly = ingolstadt_weather %>% 
  mutate(year_week = paste0(isoyear(date),"-",isoweek(date))) %>%
  group_by(year_week) %>%
  summarize(date = first(date), prcp_week = sum(prcp), tavg= mean(tavg),snow_week =sum( snow),wspd = mean(wspd),tmax = max(tmax)) %>%
  arrange(year_week)
#ingolstadt_weather_weekly = unique(weatherstack_kelheim_weekly)
print(ingolstadt_weather_weekly)
mob_joined_with_ingolstadt = ingolstadt_weather_weekly %>% 
  inner_join(snz_mobility_kelheim_year_week, by = "year_week") %>% 
  select(-date.x) %>%
  rename(date = date.y) %>%
  replace_na(list(tmax = 0))

print(mob_joined_with_ingolstadt)
#First plot with colour as precipitation
fills <- c("Ingolstadt" = "blue", "Berlin" = "red")
plt_ing_color = ggplot(mob_joined_with_ingolstadt)+
  geom_point(aes(x = date,y = not_at_home_change,colour = prcp_week,fill = "Ingolstadt"))+
  geom_point(data = mob_joined_with_weather_berlin,aes(x = date,y = not_at_home_change,colour = prcp_week,fill = "Berlin"))+
  scale_color_gradient(low = "white",high = "black")+
  scale_fill_manual(values = fills)

ggplotly(plt_ing_color)
plt_ing_color = ggplot(mob_joined_with_ingolstadt)+
  geom_point(aes(x = date,y = not_at_home_change))+
  geom_line(aes(x = date,y = prcp_week,color = "Ingolstadt"))+
  #geom_line(data = berlin_weather_weekly,aes(x = date,y = prcp_week,color = "Berlin"))+
  scale_color_manual(values = fills)+
  ggtitle("Kelheim mobility with precipitation as line plot on same axis")

ggplotly(plt_ing_color)
# replace coluumn positioning

mob_joined_with_weather_berlin = mob_joined_with_weather_berlin %>% select(year_week,prcp_week,tavg,snow_week,wspd,tmax,date,not_at_home_change)

mob_joined_with_weather_berlin = mob_joined_with_weather_berlin %>% mutate(landkreis = "Berlin")

mob_joined_with_ingolstadt = mob_joined_with_ingolstadt %>% mutate(landkreis = "Ingolstadt")

mob_joined_with_ing_berlin = rbind(mob_joined_with_ingolstadt,mob_joined_with_weather_berlin)
plt_hist_precip_ing = ggplot(mob_joined_with_ing_berlin,aes(x = prcp_week,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 2,position =  position_dodge())

ggplotly(plt_hist_precip_ing)
NA
plt_hist_precip_ing = ggplot(mob_joined_with_ing_berlin,aes(x = tavg,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 2,position = position_dodge2())

ggplotly(plt_hist_precip_ing)
NA
plt_hist_precip_ing = ggplot(mob_joined_with_ing_berlin,aes(x = tmax,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 2)

ggplotly(plt_hist_precip_ing)
NA
plt_hist_precip_ing = ggplot(mob_joined_with_ing_berlin,aes(x = snow_week,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 5)

ggplotly(plt_hist_precip_ing)
Warning: Removed 1 rows containing non-finite values (stat_summary_bin).

After first look at data, we can assume that hours out of home strongly depend on average temperature outside, that sounds logical. Mb categorization seasons of the data will help to understand this function

mob_joined_with_ing_berlin = mob_joined_with_ing_berlin %>% 
  mutate(season = ifelse(month(date) %in% c(12,1,2),"winter",NA)) %>%
  mutate(season = ifelse(month(date) %in% c(3,4,5),"spring",season)) %>%
  mutate(season = ifelse(month(date) %in% c(6,7,8),"summer",season)) %>%
  mutate(season = ifelse(month(date) %in% c(9,10,11),"autumn",season))
#insert also a data about overall in germany
plt_hist_season = ggplot(mob_joined_with_ing_berlin,aes(x = season,y = not_at_home_change,fill = landkreis))+
  stat_summary_bin(fun = "mean",
                   geom = "bar",
                   binwidth = 5,position = position_dodge())

ggplotly(plt_hist_season)

So it seems that season has an enormous impact at mobility of citizens. Another important parameter can be description of the weather based on Kelheim statistics, we will merge it into Ingolstadt weather, because of the assumption, that Ingolstadt ad Kelheim have the similar weather properties.

type_of_weather = unique(weatherstack_kelheim$description)

weatherstack_kelheim_year_week = weatherstack_kelheim %>% mutate(year_week = paste0(isoyear(date),"-",isoweek(date)))

week_description_impact = weatherstack_kelheim_year_week %>% group_by(year_week) %>% count(description)

week_description_impact = week_description_impact %>% pivot_wider(names_from = description,values_from = n)

#remove NAs
week_description_impact[is.na(week_description_impact)] = 0
print(week_description_impact)
mob_joined_with_ingolstadt_description = mob_joined_with_ingolstadt %>% inner_join(week_description_impact, by = "year_week")
#normalize it to a percentage
mob_joined_with_ingolstadt_description[type_of_weather] = mob_joined_with_ingolstadt_description[type_of_weather]/168 #168 hours a week

#Assumption not_at_home_change is calculated through individual weather impact normally
#=> each type of weather for the weak get its own weather impact based on not_at_home

#mob_joined_with_ingolstadt_description[type_of_weather] = mob_joined_with_ingolstadt_description[type_of_weather]*mob_joined_with_ingolstadt_description$not_at_home_change


print(mob_joined_with_ingolstadt_description)
mob_joined_with_ingolstadt_description_longer = mob_joined_with_ingolstadt_description%>% pivot_longer(cols = all_of(type_of_weather),names_to = "description")

description_impact_overall = mob_joined_with_ingolstadt_description_longer %>% 
  group_by(description) %>% summarize(impact = mean(value))

plot_ly(data = description_impact_overall,x = ~description,y = ~impact,type= "bar")

Take a policy data into table

Statisctical evaluation of null hypothesis, independency tests

Starting with a model

After first data preparation and analysis, let’s try to make some predicitions about not_at_home duration based on plots that shown us a major impact on not_at_home variable, like tavg, season, description, tavg. Starting with a linear model and using Ingolstadt data limited by year 2020,2021

mob_joined_with_ingolstadt_description = mob_joined_with_ingolstadt_description %>% 
  mutate(season = ifelse(month(date) %in% c(12,1,2),"winter",NA)) %>%
  mutate(season = ifelse(month(date) %in% c(3,4,5),"spring",season)) %>%
  mutate(season = ifelse(month(date) %in% c(6,7,8),"summer",season)) %>%
  mutate(season = ifelse(month(date) %in% c(9,10,11),"autumn",season))

train_data = mob_joined_with_ingolstadt_description %>% filter(year(date)<2022)

first_model = lm(not_at_home_change ~ season*prcp_week*tavg,
                 data = train_data)


test_data = mob_joined_with_ingolstadt_description %>% add_predictions(model = first_model) %>% add_residuals(model = first_model)

ggplot(test_data) +
  geom_line(aes(x = date,y = not_at_home_change,color = c("blue" = "actual")))+
  geom_line(aes(x = date,y = pred,color = c("red" = "predicted")))

LS0tDQp0aXRsZTogIkJhZCB3ZWF0aGVyIEtlbGhlaW0gRGVtbyINCmF1dGhvcjogIk9sZWtzYW5kciBTb2JvbGlldiINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0aGVtZTogY29zbW8NCiAgICBoaWdobGlnaHQ6IG1vbm9jaHJvbWUNCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cNCnJ1bnRpbWU6IHNoaW55DQplZGl0b3Jfb3B0aW9uczoNCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQoNCmBgYHtyLCBpbmNsdWRlPSBGQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkobGVhZmxldCkNCmxpYnJhcnkocm1hcmtkb3duKQ0KbGlicmFyeShtb2RlbHIpDQoNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIyAqKlJlc2VhcmNoIE1ldGVvc3RhdCoqDQoNCkFmdGVyIHNvbWUgcmVzZWFyY2hlcyBhYm91dCBtZXRlb3N0YXQgZGF0YSBuZWFyZXN0IHN0YXRpb24gaW4gREUgdGhhdCBiZWxvbmdzIHRvIEtlbGhlaW0gcmVnaW9uIGFyZToNCg0KLSAgICJNYWxsZXJzZG9yZi1QZmFmZmVuYmVyZy9OaWVkZXJiYXllcm4iIHdpdGggYW4gaWQ6ICJEMzE0NyINCi0gICAiTmV1bWFya3QgLyBIw7ZoZW5iZXJnIiB3aXRoIGFuZCBpZDogIjY5MTEwIg0KLSAgIFVlYnVuZ3Nkb3JmIC8gRW1ob2YNCg0KdGhlcmUgYXJlIG1hbnkgb2YgdGhlbSBzbyBJIGFtIHN0YXJ0aW5nIHRvIHRoaW5rIGFib3V0IGV4dHJhY3RpbmcgYWxsIGZyb20gdGhlIEJheWVybiBvciBleHRyYWN0IHRoZSBuZWFyZXN0IGZyb20gbG9uZ3RpdHVkZS9sYXRpdHVkZSBwb2ludCB3aXRoIHRoZSBLZWxoZWltIHNoYXBlZmlsZSh1c2luZyBqc29uIGFuZCBFdWNsaWQgZGlzdGFuY2VzKQ0KDQpbS2VsaGVpbSBoYXMgbm8gd2VhdGhlciBzdGF0aW9uLCBidXQgaXQgY291bGQgYmUgcmVjb25zdHJ1Y3RlZCB3aXRoIDIgb3RoZXJdKGh0dHBzOi8vd2VhdGhlcnNwYXJrLmNvbS95LzcwMzcwL0F2ZXJhZ2UtV2VhdGhlci1pbi1LZWxoZWltLUdlcm1hbnktWWVhci1Sb3VuZCkNCg0KSG9oZW5mZWxzIHdpdGggaWQ6ICIxMDc3NSIgYW5kIEluZ29sc3RhZHQgd2l0aCBpZDoiMTA4NjAiICoqa2VsaGVpbV9kYXRhID0ge3dlaWdodDF9eHtob2hlbmZlbHN9ICsge3dlaWdodDJ9eHtpbmdsc3RhZHR9KioNCg0KQWxzbyB0aGlzIHNpdGUgc2hvd3MsIHRoYXQgdGhlcmUgYXJlIG1hbnkgb2YgdGhlIEtlbGhlaW0gc3RhdGlvbnMgaW4gdGhpcyBhcmVhLCBidXQgbWV0ZW9zdGF0IGRvZXNuJ3QgY29udGFpbiB0aGVtIDxodHRwczovL3d3dy53dW5kZXJncm91bmQuY29tL2Rhc2hib2FyZC9wd3MvSUtFTEhFNT4NCg0KIyMgKipSZXNlYXJjaCBXZWF0aGVyc3RhY2sqKg0KDQpgYGB7ciBmaXJzdCBsb29rIGF0IHdlYXRoZXJzdGFjayBkYXRhIHNwZWNpZmljIHRvIEtlbGhlaW19DQp3ZWF0aGVyc3RhY2tfa2VsaGVpbSA9IHJlYWRfZGVsaW0oImRhdGEvS2VsaGVpbV93ZWF0aGVyX3NpbmNlX2p1bHlfMjAwOC5jc3YiLGRlbGltID0gIiwiKQ0KcHJpbnQod2VhdGhlcnN0YWNrX2tlbGhlaW0pDQpgYGANCg0KV2hhdCB0byB0YWtlIGFzIGEgcmVmZmVyIHBvaW50IGlzbid0IGNsZWFyIGJlY2F1c2Ugb2YgdGhlIGRhdGUoYmVmb3JlL2FmdGVyIGNvdmlkKSBhbmQgd2VhdGhlciB0eXBlIChzdW5ueSxjbGVhcix0ZW1wZXJhdHVyZSkgQWxzbyB0aGVyZSBpcyBubyB0ZW1wZXJhdHVyZSBpbiBpdCA6Lw0KDQojIyAqKkltcG9ydCBtb2JpbGl0eSBmcm9tIEdvb2dsZSoqDQoNCmBgYHtyIGluY2x1ZGluZyBnb29nbGUgZ2VybWFueSBtb2JpbGl0eSBkYXRhLG1lc3NhZ2U9RkFMU0V9DQojZ2xvYmFsX21vYmlsaXR5ID0gcmVhZF9kZWxpbSgiaHR0cHM6Ly93d3cuZ3N0YXRpYy5jb20vY292aWQxOS9tb2JpbGl0eS9HbG9iYWxfTW9iaWxpdHlfUmVwb3J0LmNzdiIsIiwiKQ0KI2RlX21vYmlsaXR5ID0gZ2xvYmFsX21vYmlsaXR5ICU+JSBmaWx0ZXIoY291bnRyeV9yZWdpb25fY29kZSA9PSAiREUiKQ0KYGBgDQoNCmBgYHtyIHdoYXQgcmVnaW9ucyBhcmUgZGF0YSBwcm92aWRlZH0NCiNwcmludCh1bmlxdWUoZGVfbW9iaWxpdHkkc3ViX3JlZ2lvbl8xKSkNCg0KYGBgDQoNCkFzIHdlIGNhbiBzZWUgdGhlIG1vc3QgcHJlY2lzZSByZWdpb24gdG8gZmlsdGVyIGRhdGEgZnJvbSBpcyBCYXZhcmlhIDovDQoNClJlbGV2YW50IGRhdGEgZm9yIHRoZSAsIG1vYmlsaXR5DQoNCmBgYHtyIG1vYmlsaXR5IGRhdGEgYmF2YXJpYX0NCiNiYXZhcmlhX21vYmlsaXR5ID0gZGVfbW9iaWxpdHkgJT4lIGZpbHRlcihzdWJfcmVnaW9uXzEgPT0gIkJhdmFyaWEiKQ0KI2JhdmFyaWFfbW9iaWxpdHkgPSBiYXZhcmlhX21vYmlsaXR5ICU+JSAjc2VsZWN0KGNvdW50cnlfcmVnaW9uLHN1Yl9yZWdpb25fMSxkYXRlLHJlc2lkZW50aWFsX3BlcmNlbnRfY2hhbmdlX2Zyb21fYmFzZWxpbmUpICU+JQ0KIyAgbXV0YXRlKHJlc2lkZW50aWFsX3BlcmNlbnRfY2hhbmdlX2Zyb21fYmFzZWxpbmUgPSAtcmVzaWRlbnRpYWxfcGVyY2VudF9jaGFuZ2VfZnJvbV9iYXNlbGluZSwNCiMgICAgICAgICBzb3VyY2UgPSAiR29vZ2xlIiklPiUNCiMgIHJlbmFtZShCdW5kZXNsYW5kSUQgPSBzdWJfcmVnaW9uXzEsbm90X2F0X2hvbWVfY2hhbmdlID0gcmVzaWRlbnRpYWxfcGVyY2VudF9jaGFuZ2VfZnJvbV9iYXNlbGluZSkNCiNiYXZhcmlhX21vYmlsaXR5ID0gYmF2YXJpYV9tb2JpbGl0eSAlPiUgc2VsZWN0KGRhdGUsQnVuZGVzbGFuZElELG5vdF9hdF9ob21lX2NoYW5nZSxzb3VyY2UpDQojTmVlZCB0byBmaWx0ZXIgb3V0IHdlZWtlbmRzDQoNCiNwbHQgPSBnZ3Bsb3QoYmF2YXJpYV9tb2JpbGl0eSkrDQojICBnZW9tX3BvaW50KGFlcyh4ID0gZGF0ZSx5ID0gbm90X2F0X2hvbWVfY2hhbmdlKSkNCiNnZ3Bsb3RseShwbHQpDQpgYGANCg0KIyMgKipJbXBvcnQgbW9iaWxpdHkgZnJvbSBTZW5vem9uKioNCg0KYGBge3IgaW1wb3J0IGZyb20gc2Vub3pvbn0NCnNuel9tb2JpbGl0eSA9IHJlYWRfZGVsaW0oImRhdGEvTEtfbW9iaWxpdHlEYXRhX3dlZWtkYXlzLmNzdiIsIjsiKQ0KDQojS2VsaGVpbQ0Kc256X21vYmlsaXR5X2tlbGhlaW0gPSBzbnpfbW9iaWxpdHkgJT4lIGZpbHRlcihMYW5ka3JlaXMgPT0gIkxhbmRrcmVpcyBLZWxoZWltIikgJT4lIG11dGF0ZShzb3VyY2UgPSAic2Vub3pvbiIpICU+JSBzZWxlY3QoLW91dE9mSG9tZUR1cmF0aW9uKSAlPiUgcmVuYW1lKG5vdF9hdF9ob21lX2NoYW5nZSA9IHBlcmNlbnRhZ2VDaGFuZ2VDb21wYXJlZFRvQmVmb3JlQ29yb25hKQ0Kc256X21vYmlsaXR5X2tlbGhlaW0kZGF0ZSA9IGFzLkRhdGUoc3RycHRpbWUoc256X21vYmlsaXR5X2tlbGhlaW0kZGF0ZSwiJVklbSVkIikpDQoNCiNCZXJsaW4NCnNuel9tb2JpbGl0eV9iZXJsaW4gPSBzbnpfbW9iaWxpdHkgJT4lIGZpbHRlcihMYW5ka3JlaXMgPT0gIkJlcmxpbiIpICU+JSBtdXRhdGUoc291cmNlID0gInNlbm96b24iKSAlPiUgc2VsZWN0KC1vdXRPZkhvbWVEdXJhdGlvbikgJT4lIHJlbmFtZShub3RfYXRfaG9tZV9jaGFuZ2UgPSBwZXJjZW50YWdlQ2hhbmdlQ29tcGFyZWRUb0JlZm9yZUNvcm9uYSkNCnNuel9tb2JpbGl0eV9iZXJsaW4kZGF0ZSA9IGFzLkRhdGUoc3RycHRpbWUoc256X21vYmlsaXR5X2JlcmxpbiRkYXRlLCIlWSVtJWQiKSkNCg0KY29sb3JzIDwtIGMoIkJlcmxpbiIgPSAiYmx1ZSIsICJLZWxoZWltIiA9ICJyZWQiKQ0KDQpwbHQgPSBnZ3Bsb3QoKSsNCiAgZ2VvbV9wb2ludChkYXRhID0gc256X21vYmlsaXR5X2tlbGhlaW0sYWVzKHggPSBkYXRlLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UsY29sb3IgPSAiQmVybGluIikpKw0KICBnZW9tX3BvaW50KGRhdGEgPSBzbnpfbW9iaWxpdHlfYmVybGluLGFlcyh4ID0gZGF0ZSx5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGNvbG9yID0gIktlbGhlaW0iKSkrDQogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY29sb3JzKQ0KZ2dwbG90bHkocGx0KQ0KYGBgDQoNCiMjICoqQWdncmVnYXRlIDIgc291cmNlcyoqDQoNCldlIHRha2UgYmVybGluIHdlYXRoZXIgZnJvbSBzdGF0aW9uIGluIFNjaG9lbmVmZWxkIHdpdGggaWQgPSAxMDM4NA0KYGBge3IgYWRkaW5nIGJlcmxpbiB3ZWF0aGVyLCBlY2hvPUZBTFNFfQ0KYmVybGluX3dlYXRoZXJfZGFpbHkgPSByZWFkX2RlbGltKCJodHRwczovL2J1bGsubWV0ZW9zdGF0Lm5ldC92Mi9kYWlseS8xMDM4NS5jc3YuZ3oiLCIsIixjb2xfbmFtZXMgPSBGQUxTRSkNCmNvbG5hbWVzKGJlcmxpbl93ZWF0aGVyX2RhaWx5KSA9IGMoImRhdGUiLCAidGF2ZyIsICJ0bWluIiwgInRtYXgiLCAicHJjcCIsICJzbm93IiwgIndkaXIiLCAid3NwZCIsICJ3cGd0IiwgInByZXMiLCAidHN1biIpDQpgYGANCg0KDQpgYGB7ciBkZWZpbmUgd2VhdGhlciBkYXRhIGFzIHdlZWsgZGF0YX0NCiMgdGhpbmsgYWJvdXQgZHVyYXRpb24gb2YgZGVzY3JpcHRpb24gY29sdW1uDQojIEtlbGhlaW0NCndlYXRoZXJzdGFja19rZWxoZWltX2RhaWx5ID0gd2VhdGhlcnN0YWNrX2tlbGhlaW0gJT4lDQogIGdyb3VwX2J5KGRhdGUpICU+JQ0KICBzdW1tYXJpemUoZGVzY3JpcHRpb24gPSBkZXNjcmlwdGlvbixwcmVjaXBfZGF5ID0gc3VtKHByZWNpcCksdmlzaWJpbGl0eV9tZWFuID0gbWVhbih2aXNpYmlsaXR5KSx0b3RhbHNub3dfZGFpbHkgPSBtZWFuKHRvdGFsc25vd19kYWlseSkpDQogIA0Kd2VhdGhlcnN0YWNrX2tlbGhlaW1fd2Vla2x5ID0gd2VhdGhlcnN0YWNrX2tlbGhlaW1fZGFpbHkgJT4lIA0KICBtdXRhdGUoeWVhcl93ZWVrID0gcGFzdGUwKGlzb3llYXIoZGF0ZSksIi0iLGlzb3dlZWsoZGF0ZSkpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcl93ZWVrKSAlPiUNCiAgc3VtbWFyaXplKGRlc2NyaXB0aW9uID0gZGVzY3JpcHRpb24sZGF0ZSA9IGZpcnN0KGRhdGUpLCBwcmVjaXBfd2VlayA9IHN1bShwcmVjaXBfZGF5KSx2aXNpYmlsaXR5X21lYW4gPSBtZWFuKHZpc2liaWxpdHlfbWVhbiksdG90YWxzbm93X3dlZWtseSA9c3VtKCB0b3RhbHNub3dfZGFpbHkpKQ0Kd2VhdGhlcnN0YWNrX2tlbGhlaW1fd2Vla2x5ID0gdW5pcXVlKHdlYXRoZXJzdGFja19rZWxoZWltX3dlZWtseSkNCnByaW50KHdlYXRoZXJzdGFja19rZWxoZWltX3dlZWtseSkNCg0KI0Jlcmxpbg0KICANCmJlcmxpbl93ZWF0aGVyX3dlZWtseSA9IGJlcmxpbl93ZWF0aGVyX2RhaWx5ICU+JSBmaWx0ZXIoeWVhcihkYXRlKSA+PTIwMjApICU+JQ0KICBtdXRhdGUoeWVhcl93ZWVrID0gcGFzdGUwKGlzb3llYXIoZGF0ZSksIi0iLGlzb3dlZWsoZGF0ZSkpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcl93ZWVrKSAlPiUNCiAgc3VtbWFyaXplKGRhdGUgPSBmaXJzdChkYXRlKSwgcHJjcF93ZWVrID0gc3VtKHByY3ApLCB0YXZnPSBtZWFuKHRhdmcpLHNub3dfd2VlayA9c3VtKCBzbm93KSx3c3BkID0gbWVhbih3c3BkKSx0bWF4ID0gbWF4KHRtYXgpKSAlPiUNCiAgYXJyYW5nZSh5ZWFyX3dlZWspDQpwcmludChiZXJsaW5fd2VhdGhlcl93ZWVrbHkpDQogIA0KYGBgDQoNCmBgYHtyIGdvb2dsZStzZW5vem9uK3dlYXRoZXJ9DQojbW9iX2pvaW5lZCA9IHJiaW5kKHNuel9tb2JpbGl0eV9rZWxoZWltLGJhdmFyaWFfbW9iaWxpdHkpDQojS2VsaGVpbQ0Kc256X21vYmlsaXR5X2tlbGhlaW1feWVhcl93ZWVrID0gc256X21vYmlsaXR5X2tlbGhlaW0gJT4lIA0KICBtdXRhdGUoeWVhcl93ZWVrID0gcGFzdGUwKGlzb3llYXIoZGF0ZSksIi0iLGlzb3dlZWsoZGF0ZSkpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcl93ZWVrKSAlPiUNCiAgc3VtbWFyaXplKGRhdGUgPSBmaXJzdChkYXRlKSxub3RfYXRfaG9tZV9jaGFuZ2UgPSBtZWFuKG5vdF9hdF9ob21lX2NoYW5nZSkpDQptb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9rZWxoZWltID0gc256X21vYmlsaXR5X2tlbGhlaW1feWVhcl93ZWVrICU+JSBpbm5lcl9qb2luKHdlYXRoZXJzdGFja19rZWxoZWltX3dlZWtseSwgYnkgPSAieWVhcl93ZWVrIikgJT4lIHNlbGVjdCgtZGF0ZS55KSAlPiUgcmVuYW1lKGRhdGUgPSBkYXRlLngpDQpwcmludChtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9rZWxoZWltKQ0KDQojQmVybGluDQpzbnpfbW9iaWxpdHlfYmVybGluX3llYXJfd2VlayA9IHNuel9tb2JpbGl0eV9iZXJsaW4gJT4lIA0KICBtdXRhdGUoeWVhcl93ZWVrID0gcGFzdGUwKGlzb3llYXIoZGF0ZSksIi0iLGlzb3dlZWsoZGF0ZSkpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcl93ZWVrKSAlPiUNCiAgc3VtbWFyaXplKGRhdGUgPSBmaXJzdChkYXRlKSxub3RfYXRfaG9tZV9jaGFuZ2UgPSBtZWFuKG5vdF9hdF9ob21lX2NoYW5nZSkpDQptb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4gPSBzbnpfbW9iaWxpdHlfYmVybGluX3llYXJfd2VlayAlPiUgaW5uZXJfam9pbihiZXJsaW5fd2VhdGhlcl93ZWVrbHksIGJ5ID0gInllYXJfd2VlayIpICU+JSBzZWxlY3QoLWRhdGUueSkgJT4lIHJlbmFtZShkYXRlID0gZGF0ZS54KQ0KcHJpbnQobW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfYmVybGluKQ0KYGBgDQoNCmBgYHtyIGZpcnN0IHBsb3R9DQojRmlyc3QgcGxvdCB3aXRoIGNvbG91ciBhcyBwcmVjaXBpdGF0aW9uDQpzaGFwZXMgPC0gYygiQmVybGluIiA9IDUsICJLZWxoZWltIiA9IDMpDQpwbHRfY29sb3IgPSBnZ3Bsb3QoKSsNCiAgZ2VvbV9wb2ludChkYXRhID0gbW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfa2VsaGVpbSxhZXMoeCA9IGRhdGUseSA9IG5vdF9hdF9ob21lX2NoYW5nZSxjb2xvdXIgPSBwcmVjaXBfd2VlayxzaGFwZSA9ICJLZWxoZWltIikpKw0KICAjZ2VvbV9wb2ludChkYXRhID0gbW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfYmVybGluLGFlcyh4ID0gZGF0ZSx5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGNvbG91ciA9IHByY3Bfd2VlayxzaGFwZSA9ICJCZXJsaW4iKSkrDQogIHNjYWxlX2NvbG9yX2dyYWRpZW50MigpKw0KICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gc2hhcGVzKQ0KICANCg0KZ2dwbG90bHkocGx0X2NvbG9yKQ0KYGBgDQoNCmBgYHtyIHNlY29uZCBwbG90fQ0KI1NlY29uZCBwbG90IGFzIGFub3RoZXIgbGluZSBhcyBwcmVjaXBpdGF0aW9uDQpwbHRfbGluZSA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9rZWxoZWltKSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRhdGUseSA9IG5vdF9hdF9ob21lX2NoYW5nZSkpKw0KICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLHkgPSBwcmVjaXBfd2VlayowLjUsY29sb3IgPSAicmVkIikpDQogIA0KDQpnZ3Bsb3RseShwbHRfbGluZSkNCmBgYA0KDQpgYGB7ciBwcmVjaXBpdGF0aW9uIGhpc3RvZ3JhbX0NCnBsdF9oaXN0X3ByZWNpcCA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9rZWxoZWltLGFlcyh4ID0gcHJlY2lwX3dlZWsseSA9IG5vdF9hdF9ob21lX2NoYW5nZSkpKw0KICBzdGF0X3N1bW1hcnlfYmluKGZ1biA9ICJtZWFuIiwNCiAgICAgICAgICAgICAgICAgICBnZW9tID0gImJhciIsDQogICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAyLGZpbGwgPSAiYmx1ZSIpDQoNCmdncGxvdGx5KHBsdF9oaXN0X3ByZWNpcCkNCmBgYA0KDQpgYGB7ciB2aXNpYmlsaXR5IGhpc3RvZ3JhbX0NCnBsdF9oaXN0X3Zpc2liaWxpdHkgPSBnZ3Bsb3QobW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfa2VsaGVpbSxhZXMoeCA9IHZpc2liaWxpdHlfbWVhbix5ID0gbm90X2F0X2hvbWVfY2hhbmdlKSkrDQogIHN0YXRfc3VtbWFyeV9iaW4oZnVuID0gIm1lYW4iLA0KICAgICAgICAgICAgICAgICAgIGdlb20gPSAiYmFyIiwNCiAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDAuNSxmaWxsID0gImJsdWUiKQ0KDQpnZ3Bsb3RseShwbHRfaGlzdF92aXNpYmlsaXR5KQ0KYGBgDQoNCmBgYHtyIHRvdGFsc25vdyBoaXN0b2dyYW19DQpwbHRfaGlzdF90b3RhbHNub3cgPSBnZ3Bsb3QobW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfa2VsaGVpbSxhZXMoeCA9IHRvdGFsc25vd193ZWVrbHkseSA9IG5vdF9hdF9ob21lX2NoYW5nZSkpKw0KICBzdGF0X3N1bW1hcnlfYmluKGZ1biA9ICJtZWFuIiwNCiAgICAgICAgICAgICAgICAgICBnZW9tID0gImJhciIsDQogICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSA3LGZpbGwgPSAiYmx1ZSIpDQoNCmdncGxvdGx5KHBsdF9oaXN0X3RvdGFsc25vdykNCmBgYA0KDQpgYGB7ciBsb29raW5nIGF0IGRlc2NyaXB0aW9uIGNvbHVtbn0NCiN0aGlzIGlzIGEgYmFkIHBsb3QgYmVjYXVzZSBpdCB0YWtlcyBkZXNjcmlwdGlvbiBvZiAxIGRheSBvZiB0aGUgd2Vlaw0KcGx0X2hpc3RfZGVzY3IgPSBnZ3Bsb3QobW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfa2VsaGVpbSxhZXMoeCA9IGRlc2NyaXB0aW9uLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UpKSsNCiAgc3RhdF9zdW1tYXJ5X2JpbihmdW4gPSAibWVhbiIsDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJiYXIiLA0KICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gNSxmaWxsID0gImJsdWUiKSsNCiAgY29vcmRfZmxpcCgpDQoNCmdncGxvdGx5KHBsdF9oaXN0X2Rlc2NyKQ0KYGBgDQoNCiMjIExldCdzIHRyeSBpdCBvdXQgd2l0aCBtZXRlb3N0YXQgZGF0YSwgdGhhdCBjb250YWlucyBzY29wZSBvZiB0aGUgMjAyMiB3aXRob3V0IHJlc3RpY3Rpb25zDQoNCkluZ29sc3RhZHQgZGF0YSBmcm9tIGlkID0gMTA4NjAgc3RhdGlvbg0KDQpgYGB7ciBJbmdvbHN0YWR0IGRhdGF9DQppbmdvbHN0YWR0X3dlYXRoZXIgPSByZWFkX2RlbGltKCJodHRwczovL2J1bGsubWV0ZW9zdGF0Lm5ldC92Mi9kYWlseS8xMDg2MC5jc3YuZ3oiLCIsIixjb2xfbmFtZXMgPSBGQUxTRSkNCg0KY29sbmFtZXMoaW5nb2xzdGFkdF93ZWF0aGVyKSA9IGMoImRhdGUiLCAidGF2ZyIsICJ0bWluIiwgInRtYXgiLCAicHJjcCIsICJzbm93IiwgIndkaXIiLCAid3NwZCIsICJ3cGd0IiwgInByZXMiLCAidHN1biIpDQoNCg0KIyBXZSBkb24ndCBuZWVkIGRhdGEgb2Ygd2VhdGhlciBiZWZvcmUgMjAyMCwgYmVjYXVzZSBvZiBzbnpfbW9iaWxpdHkgZGF0ZSwgYWxzbyBkYXRhIGlzbid0IHByZWNpc2UNCg0KaW5nb2xzdGFkdF93ZWF0aGVyID0gaW5nb2xzdGFkdF93ZWF0aGVyICU+JSBmaWx0ZXIoeWVhcihkYXRlKT49MjAyMCklPiUgcmVwbGFjZV9uYShsaXN0KHNub3cgPSAwKSkNCg0KcHJpbnQoaW5nb2xzdGFkdF93ZWF0aGVyKQ0KYGBgDQoNCkhvaGVuZmVscyBkYXRhIGZyb20gaWQgPSAxMDc3NSBzdGF0aW9uDQoNCmBgYHtyIEhvaGVuZmVscyBkYXRhfQ0KaG9oZW5mZWxzX3dlYXRoZXIgPSByZWFkX2RlbGltKCJodHRwczovL2J1bGsubWV0ZW9zdGF0Lm5ldC92Mi9kYWlseS8xMDc3NS5jc3YuZ3oiLCIsIixjb2xfbmFtZXMgPSBGQUxTRSkNCg0KY29sbmFtZXMoaG9oZW5mZWxzX3dlYXRoZXIpID0gYygiZGF0ZSIsICJ0YXZnIiwgInRtaW4iLCAidG1heCIsICJwcmNwIiwgInNub3ciLCAid2RpciIsICJ3c3BkIiwgIndwZ3QiLCAicHJlcyIsICJ0c3VuIikNCg0KDQojIFdlIGRvbid0IG5lZWQgZGF0YSBvZiB3ZWF0aGVyIGJlZm9yZSAyMDIwLCBiZWNhdXNlIG9mIHNuel9tb2JpbGl0eSBkYXRlLCBhbHNvIGRhdGEgaXNuJ3QgcHJlY2lzZQ0KDQpob2hlbmZlbHNfd2VhdGhlciA9IGhvaGVuZmVsc193ZWF0aGVyICU+JSBmaWx0ZXIoeWVhcihkYXRlKT49MjAyMCkgJT4lIHJlcGxhY2VfbmEobGlzdChzbm93ID0gMCkpDQoNCnByaW50KGhvaGVuZmVsc193ZWF0aGVyKQ0KYGBgDQoNCkFzIHdlIGNhbiBzZWUgaW4gSG9oZW5mZWxzIGRhdGEgaXNuJ3QgdGhhdCBhY2N1cmF0ZSBhbmQgcHJlY2lwaXRhdGlvbiBpcyBkYXRhIGlzIG1pc3NpbmcgZnIgeWVhciAyMDIwLCBzbyBmb3IgdGhlIGZ1cnRoZXIgYW5hbHlzaXMgd2UgdGFrZSBvbmx5IEluZ29sc3RhZHQgZGF0YS4NCg0KYGBge3IgSW5nb2xzdGFkdCB3ZWVrbHkgZGF0YX0NCmluZ29sc3RhZHRfd2VhdGhlcl93ZWVrbHkgPSBpbmdvbHN0YWR0X3dlYXRoZXIgJT4lIA0KICBtdXRhdGUoeWVhcl93ZWVrID0gcGFzdGUwKGlzb3llYXIoZGF0ZSksIi0iLGlzb3dlZWsoZGF0ZSkpKSAlPiUNCiAgZ3JvdXBfYnkoeWVhcl93ZWVrKSAlPiUNCiAgc3VtbWFyaXplKGRhdGUgPSBmaXJzdChkYXRlKSwgcHJjcF93ZWVrID0gc3VtKHByY3ApLCB0YXZnPSBtZWFuKHRhdmcpLHNub3dfd2VlayA9c3VtKCBzbm93KSx3c3BkID0gbWVhbih3c3BkKSx0bWF4ID0gbWF4KHRtYXgpKSAlPiUNCiAgYXJyYW5nZSh5ZWFyX3dlZWspDQojaW5nb2xzdGFkdF93ZWF0aGVyX3dlZWtseSA9IHVuaXF1ZSh3ZWF0aGVyc3RhY2tfa2VsaGVpbV93ZWVrbHkpDQpwcmludChpbmdvbHN0YWR0X3dlYXRoZXJfd2Vla2x5KQ0KYGBgDQoNCmBgYHtyIGFnZ3JlZ2F0ZSBzbnogYW5kIGluZ29sc3RhZHR9DQptb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdCA9IGluZ29sc3RhZHRfd2VhdGhlcl93ZWVrbHkgJT4lIA0KICBpbm5lcl9qb2luKHNuel9tb2JpbGl0eV9rZWxoZWltX3llYXJfd2VlaywgYnkgPSAieWVhcl93ZWVrIikgJT4lIA0KICBzZWxlY3QoLWRhdGUueCkgJT4lDQogIHJlbmFtZShkYXRlID0gZGF0ZS55KSAlPiUNCiAgcmVwbGFjZV9uYShsaXN0KHRtYXggPSAwKSkNCg0KcHJpbnQobW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHQpDQpgYGANCg0KYGBge3IgY29sb3JlZCBwcmVjaXBpdGF0aW9uIHBsb3QgSW5nb2xzdGFkdH0NCiNGaXJzdCBwbG90IHdpdGggY29sb3VyIGFzIHByZWNpcGl0YXRpb24NCmZpbGxzIDwtIGMoIkluZ29sc3RhZHQiID0gImJsdWUiLCAiQmVybGluIiA9ICJyZWQiKQ0KcGx0X2luZ19jb2xvciA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdCkrDQogIGdlb21fcG9pbnQoYWVzKHggPSBkYXRlLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UsY29sb3VyID0gcHJjcF93ZWVrLGZpbGwgPSAiSW5nb2xzdGFkdCIpKSsNCiAgZ2VvbV9wb2ludChkYXRhID0gbW9iX2pvaW5lZF93aXRoX3dlYXRoZXJfYmVybGluLGFlcyh4ID0gZGF0ZSx5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGNvbG91ciA9IHByY3Bfd2VlayxmaWxsID0gIkJlcmxpbiIpKSsNCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobG93ID0gIndoaXRlIixoaWdoID0gImJsYWNrIikrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGZpbGxzKQ0KDQpnZ3Bsb3RseShwbHRfaW5nX2NvbG9yKQ0KYGBgDQoNCg0KYGBge3IgcHJlY2lwaXRhdGlvbiBhcyBsaW5lIHBsb3R9DQpwbHRfaW5nX2NvbG9yID0gZ2dwbG90KG1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0KSsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRhdGUseSA9IG5vdF9hdF9ob21lX2NoYW5nZSkpKw0KICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLHkgPSBwcmNwX3dlZWssY29sb3IgPSAiSW5nb2xzdGFkdCIpKSsNCiAgI2dlb21fbGluZShkYXRhID0gYmVybGluX3dlYXRoZXJfd2Vla2x5LGFlcyh4ID0gZGF0ZSx5ID0gcHJjcF93ZWVrLGNvbG9yID0gIkJlcmxpbiIpKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGZpbGxzKSsNCiAgZ2d0aXRsZSgiS2VsaGVpbSBtb2JpbGl0eSB3aXRoIHByZWNpcGl0YXRpb24gYXMgbGluZSBwbG90IG9uIHNhbWUgYXhpcyIpDQoNCmdncGxvdGx5KHBsdF9pbmdfY29sb3IpDQpgYGANCg0KYGBge3Igam9pbiBpbmdvbHN0YWR0IGFuZCBiZXJsaW4gaW4gMSB0YWJsZX0NCiMgcmVwbGFjZSBjb2x1dW1uIHBvc2l0aW9uaW5nDQoNCm1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2JlcmxpbiA9IG1vYl9qb2luZWRfd2l0aF93ZWF0aGVyX2JlcmxpbiAlPiUgc2VsZWN0KHllYXJfd2VlayxwcmNwX3dlZWssdGF2Zyxzbm93X3dlZWssd3NwZCx0bWF4LGRhdGUsbm90X2F0X2hvbWVfY2hhbmdlKQ0KDQptb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4gPSBtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4gJT4lIG11dGF0ZShsYW5ka3JlaXMgPSAiQmVybGluIikNCg0KbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHQgPSBtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdCAlPiUgbXV0YXRlKGxhbmRrcmVpcyA9ICJJbmdvbHN0YWR0IikNCg0KbW9iX2pvaW5lZF93aXRoX2luZ19iZXJsaW4gPSByYmluZChtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdCxtb2Jfam9pbmVkX3dpdGhfd2VhdGhlcl9iZXJsaW4pDQpgYGANCg0KYGBge3IgaGlzdG9ncmFtIHdpdGggcHJlY2lwfQ0KcGx0X2hpc3RfcHJlY2lwX2luZyA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbixhZXMoeCA9IHByY3Bfd2Vlayx5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGZpbGwgPSBsYW5ka3JlaXMpKSsNCiAgc3RhdF9zdW1tYXJ5X2JpbihmdW4gPSAibWVhbiIsDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJiYXIiLA0KICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gMixwb3NpdGlvbiA9ICBwb3NpdGlvbl9kb2RnZSgpKQ0KDQpnZ3Bsb3RseShwbHRfaGlzdF9wcmVjaXBfaW5nKQ0KDQpgYGANCg0KYGBge3IgaGlzdG9ncmFtIHdpdGggYXZlcmFnZSB0ZW1wZXJhdHVyZX0NCnBsdF9oaXN0X3ByZWNpcF9pbmcgPSBnZ3Bsb3QobW9iX2pvaW5lZF93aXRoX2luZ19iZXJsaW4sYWVzKHggPSB0YXZnLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UsZmlsbCA9IGxhbmRrcmVpcykpKw0KICBzdGF0X3N1bW1hcnlfYmluKGZ1biA9ICJtZWFuIiwNCiAgICAgICAgICAgICAgICAgICBnZW9tID0gImJhciIsDQogICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSAyLHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UyKCkpDQoNCmdncGxvdGx5KHBsdF9oaXN0X3ByZWNpcF9pbmcpDQoNCmBgYA0KDQpgYGB7ciBoaXN0b2dyYW0gd2l0aCBtYXhpbWFsIHRlbXBlcmF0dXJlfQ0KcGx0X2hpc3RfcHJlY2lwX2luZyA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbixhZXMoeCA9IHRtYXgseSA9IG5vdF9hdF9ob21lX2NoYW5nZSxmaWxsID0gbGFuZGtyZWlzKSkrDQogIHN0YXRfc3VtbWFyeV9iaW4oZnVuID0gIm1lYW4iLA0KICAgICAgICAgICAgICAgICAgIGdlb20gPSAiYmFyIiwNCiAgICAgICAgICAgICAgICAgICBiaW53aWR0aCA9IDIpDQoNCmdncGxvdGx5KHBsdF9oaXN0X3ByZWNpcF9pbmcpDQoNCmBgYA0KDQpgYGB7ciBoaXN0b2dyYW0gd2l0aCBzbm93IGF0IHRoZSB3ZWVrfQ0KcGx0X2hpc3RfcHJlY2lwX2luZyA9IGdncGxvdChtb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbixhZXMoeCA9IHNub3dfd2Vlayx5ID0gbm90X2F0X2hvbWVfY2hhbmdlLGZpbGwgPSBsYW5ka3JlaXMpKSsNCiAgc3RhdF9zdW1tYXJ5X2JpbihmdW4gPSAibWVhbiIsDQogICAgICAgICAgICAgICAgICAgZ2VvbSA9ICJiYXIiLA0KICAgICAgICAgICAgICAgICAgIGJpbndpZHRoID0gNSkNCg0KZ2dwbG90bHkocGx0X2hpc3RfcHJlY2lwX2luZykNCg0KYGBgDQoNCkFmdGVyIGZpcnN0IGxvb2sgYXQgZGF0YSwgd2UgY2FuIGFzc3VtZSB0aGF0IGhvdXJzIG91dCBvZiBob21lIHN0cm9uZ2x5IGRlcGVuZCBvbiBhdmVyYWdlIHRlbXBlcmF0dXJlIG91dHNpZGUsIHRoYXQgc291bmRzIGxvZ2ljYWwuIE1iIGNhdGVnb3JpemF0aW9uIHNlYXNvbnMgb2YgdGhlIGRhdGEgd2lsbCBoZWxwIHRvIHVuZGVyc3RhbmQgdGhpcyBmdW5jdGlvbg0KDQpgYGB7ciBhZGRpbmcgbmV3IHNlYXNvbiBjb2x1bW59DQptb2Jfam9pbmVkX3dpdGhfaW5nX2JlcmxpbiA9IG1vYl9qb2luZWRfd2l0aF9pbmdfYmVybGluICU+JSANCiAgbXV0YXRlKHNlYXNvbiA9IGlmZWxzZShtb250aChkYXRlKSAlaW4lIGMoMTIsMSwyKSwid2ludGVyIixOQSkpICU+JQ0KICBtdXRhdGUoc2Vhc29uID0gaWZlbHNlKG1vbnRoKGRhdGUpICVpbiUgYygzLDQsNSksInNwcmluZyIsc2Vhc29uKSkgJT4lDQogIG11dGF0ZShzZWFzb24gPSBpZmVsc2UobW9udGgoZGF0ZSkgJWluJSBjKDYsNyw4KSwic3VtbWVyIixzZWFzb24pKSAlPiUNCiAgbXV0YXRlKHNlYXNvbiA9IGlmZWxzZShtb250aChkYXRlKSAlaW4lIGMoOSwxMCwxMSksImF1dHVtbiIsc2Vhc29uKSkNCmBgYA0KDQoNCmBgYHtyIHNlYXNvbiB+IG5vdF9hdF9ob21lOiAgcGxvdHN9DQojaW5zZXJ0IGFsc28gYSBkYXRhIGFib3V0IG92ZXJhbGwgaW4gZ2VybWFueQ0KcGx0X2hpc3Rfc2Vhc29uID0gZ2dwbG90KG1vYl9qb2luZWRfd2l0aF9pbmdfYmVybGluLGFlcyh4ID0gc2Vhc29uLHkgPSBub3RfYXRfaG9tZV9jaGFuZ2UsZmlsbCA9IGxhbmRrcmVpcykpKw0KICBzdGF0X3N1bW1hcnlfYmluKGZ1biA9ICJtZWFuIiwNCiAgICAgICAgICAgICAgICAgICBnZW9tID0gImJhciIsDQogICAgICAgICAgICAgICAgICAgYmlud2lkdGggPSA1LHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSkNCg0KZ2dwbG90bHkocGx0X2hpc3Rfc2Vhc29uKQ0KYGBgDQoNCg0KU28gaXQgc2VlbXMgdGhhdCBzZWFzb24gaGFzIGFuIGVub3Jtb3VzIGltcGFjdCBhdCBtb2JpbGl0eSBvZiBjaXRpemVucy4NCkFub3RoZXIgaW1wb3J0YW50IHBhcmFtZXRlciBjYW4gYmUgZGVzY3JpcHRpb24gb2YgdGhlIHdlYXRoZXIgYmFzZWQgb24gS2VsaGVpbSBzdGF0aXN0aWNzLCB3ZSB3aWxsIG1lcmdlIGl0IGludG8gSW5nb2xzdGFkdCB3ZWF0aGVyLCBiZWNhdXNlIG9mIHRoZSBhc3N1bXB0aW9uLCB0aGF0IEluZ29sc3RhZHQgYWQgS2VsaGVpbSBoYXZlIHRoZSBzaW1pbGFyIHdlYXRoZXIgcHJvcGVydGllcy4NCg0KYGBge3IgYWRkaW5nIGRlc2NyaXB0aW9uIHRvIGFuIGluZ29sc3RhZHQgZGF0YX0NCnR5cGVfb2Zfd2VhdGhlciA9IHVuaXF1ZSh3ZWF0aGVyc3RhY2tfa2VsaGVpbSRkZXNjcmlwdGlvbikNCg0Kd2VhdGhlcnN0YWNrX2tlbGhlaW1feWVhcl93ZWVrID0gd2VhdGhlcnN0YWNrX2tlbGhlaW0gJT4lIG11dGF0ZSh5ZWFyX3dlZWsgPSBwYXN0ZTAoaXNveWVhcihkYXRlKSwiLSIsaXNvd2VlayhkYXRlKSkpDQoNCndlZWtfZGVzY3JpcHRpb25faW1wYWN0ID0gd2VhdGhlcnN0YWNrX2tlbGhlaW1feWVhcl93ZWVrICU+JSBncm91cF9ieSh5ZWFyX3dlZWspICU+JSBjb3VudChkZXNjcmlwdGlvbikNCg0Kd2Vla19kZXNjcmlwdGlvbl9pbXBhY3QgPSB3ZWVrX2Rlc2NyaXB0aW9uX2ltcGFjdCAlPiUgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGRlc2NyaXB0aW9uLHZhbHVlc19mcm9tID0gbikNCg0KI3JlbW92ZSBOQXMNCndlZWtfZGVzY3JpcHRpb25faW1wYWN0W2lzLm5hKHdlZWtfZGVzY3JpcHRpb25faW1wYWN0KV0gPSAwDQpwcmludCh3ZWVrX2Rlc2NyaXB0aW9uX2ltcGFjdCkNCmBgYA0KYGBge3Igam9pbiBpdCB3aXRoIG1vYmlsaXR5IGRhdGEgSW5nb2xzdGFkdH0NCm1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0X2Rlc2NyaXB0aW9uID0gbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHQgJT4lIGlubmVyX2pvaW4od2Vla19kZXNjcmlwdGlvbl9pbXBhY3QsIGJ5ID0gInllYXJfd2VlayIpDQojbm9ybWFsaXplIGl0IHRvIGEgcGVyY2VudGFnZQ0KbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25bdHlwZV9vZl93ZWF0aGVyXSA9IG1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0X2Rlc2NyaXB0aW9uW3R5cGVfb2Zfd2VhdGhlcl0vMTY4ICMxNjggaG91cnMgYSB3ZWVrDQoNCiNBc3N1bXB0aW9uIG5vdF9hdF9ob21lX2NoYW5nZSBpcyBjYWxjdWxhdGVkIHRocm91Z2ggaW5kaXZpZHVhbCB3ZWF0aGVyIGltcGFjdCBub3JtYWxseQ0KIz0+IGVhY2ggdHlwZSBvZiB3ZWF0aGVyIGZvciB0aGUgd2VhayBnZXQgaXRzIG93biB3ZWF0aGVyIGltcGFjdCBiYXNlZCBvbiBub3RfYXRfaG9tZQ0KDQojbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25bdHlwZV9vZl93ZWF0aGVyXSA9IG1vYl9qb2luZWRfd2l0aF9pbmdvbHN0YWR0X2Rlc2NyaXB0aW9uW3R5cGVfb2Zfd2VhdGhlcl0qbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb24kbm90X2F0X2hvbWVfY2hhbmdlDQoNCg0KcHJpbnQobW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb24pDQpgYGANCg0KDQpgYGB7ciBsZXRzIGJhcmNoYXJ0IGl0IDopfQ0KbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25fbG9uZ2VyID0gbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb24lPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBhbGxfb2YodHlwZV9vZl93ZWF0aGVyKSxuYW1lc190byA9ICJkZXNjcmlwdGlvbiIpDQoNCmRlc2NyaXB0aW9uX2ltcGFjdF9vdmVyYWxsID0gbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb25fbG9uZ2VyICU+JSANCiAgZ3JvdXBfYnkoZGVzY3JpcHRpb24pICU+JSBzdW1tYXJpemUoaW1wYWN0ID0gbWVhbih2YWx1ZSkpDQoNCnBsb3RfbHkoZGF0YSA9IGRlc2NyaXB0aW9uX2ltcGFjdF9vdmVyYWxsLHggPSB+ZGVzY3JpcHRpb24seSA9IH5pbXBhY3QsdHlwZT0gImJhciIpDQpgYGANCg0KIyMgKipUYWtlIGEgcG9saWN5IGRhdGEgaW50byB0YWJsZSoqDQojIyAqKlN0YXRpc2N0aWNhbCBldmFsdWF0aW9uIG9mIG51bGwgaHlwb3RoZXNpcywgaW5kZXBlbmRlbmN5IHRlc3RzKioNCiMjICoqU3RhcnRpbmcgd2l0aCBhIG1vZGVsKioNCg0KQWZ0ZXIgZmlyc3QgZGF0YSBwcmVwYXJhdGlvbiBhbmQgYW5hbHlzaXMsIGxldCdzIHRyeSB0byBtYWtlIHNvbWUgcHJlZGljaXRpb25zIGFib3V0IG5vdF9hdF9ob21lIGR1cmF0aW9uIGJhc2VkIG9uIHBsb3RzIHRoYXQgc2hvd24gdXMgYSBtYWpvciBpbXBhY3Qgb24gbm90X2F0X2hvbWUgdmFyaWFibGUsIGxpa2UgKnRhdmcqLCAqc2Vhc29uKiwgKmRlc2NyaXB0aW9uKiwgKnRhdmcqLiBTdGFydGluZyB3aXRoIGEgKmxpbmVhciBtb2RlbCogYW5kIHVzaW5nICpJbmdvbHN0YWR0IGRhdGEqIGxpbWl0ZWQgYnkgeWVhciAyMDIwLDIwMjENCg0KYGBge3IgbW9kZWxyfQ0KbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb24gPSBtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbiAlPiUgDQogIG11dGF0ZShzZWFzb24gPSBpZmVsc2UobW9udGgoZGF0ZSkgJWluJSBjKDEyLDEsMiksIndpbnRlciIsTkEpKSAlPiUNCiAgbXV0YXRlKHNlYXNvbiA9IGlmZWxzZShtb250aChkYXRlKSAlaW4lIGMoMyw0LDUpLCJzcHJpbmciLHNlYXNvbikpICU+JQ0KICBtdXRhdGUoc2Vhc29uID0gaWZlbHNlKG1vbnRoKGRhdGUpICVpbiUgYyg2LDcsOCksInN1bW1lciIsc2Vhc29uKSkgJT4lDQogIG11dGF0ZShzZWFzb24gPSBpZmVsc2UobW9udGgoZGF0ZSkgJWluJSBjKDksMTAsMTEpLCJhdXR1bW4iLHNlYXNvbikpDQoNCnRyYWluX2RhdGEgPSBtb2Jfam9pbmVkX3dpdGhfaW5nb2xzdGFkdF9kZXNjcmlwdGlvbiAlPiUgZmlsdGVyKHllYXIoZGF0ZSk8MjAyMikNCg0KZmlyc3RfbW9kZWwgPSBsbShub3RfYXRfaG9tZV9jaGFuZ2UgfiBzZWFzb24qcHJjcF93ZWVrKnRhdmcsDQogICAgICAgICAgICAgICAgIGRhdGEgPSB0cmFpbl9kYXRhKQ0KDQpgYGANCg0KYGBge3IgcHJlZGljdGlvbnN9DQoNCg0KdGVzdF9kYXRhID0gbW9iX2pvaW5lZF93aXRoX2luZ29sc3RhZHRfZGVzY3JpcHRpb24gJT4lIGFkZF9wcmVkaWN0aW9ucyhtb2RlbCA9IGZpcnN0X21vZGVsKSAlPiUgYWRkX3Jlc2lkdWFscyhtb2RlbCA9IGZpcnN0X21vZGVsKQ0KDQpnZ3Bsb3QodGVzdF9kYXRhKSArDQogIGdlb21fbGluZShhZXMoeCA9IGRhdGUseSA9IG5vdF9hdF9ob21lX2NoYW5nZSxjb2xvciA9IGMoImJsdWUiKSkpKw0KICBnZW9tX2xpbmUoYWVzKHggPSBkYXRlLHkgPSBwcmVkLGNvbG9yID0gInJlZCIpKQ0KDQpgYGANCg0KDQo=